#include "teststdafx.h"
#include "stdafx.h"

#pragma warning(disable:4996)

#include <gtest/gtest.h>

#include <ostream>
#include "Thread/BusinessThread.h"
#include "Business/IMarketHandler.h"
#include "Business/Business.h"
#include "Business/Command.h"
#include "Business/Market.h"
#include "Business/Dealing.h"
#include "Utils/FastBlockQueue.h"
#include "Repository/DealingRepository.h"
#include "Business/QueueContext.h"
#include "Service/MarketStorage.h"
#include "Business/Customer.h"
#include "Business/RequestTimeout.h"
#include "Business/OrderNotification.h"
#include "Business/ContractMatcher.h"
#include "Utils/TimeoutNotifier.h"


namespace {
class MockMarketHandler : public IMarketHandler
{
public:
	virtual ~MockMarketHandler() {}

	bool IsEmpty() const
	{
		return m_markets.empty();
	}

	Market const& GetLastMarket() const
	{
		return m_markets.back();
	}
private:
	virtual void DoOnMarket(Market const& market)
	{
		m_markets.push_back(market);
	}
	std::vector<Market> m_markets;
};

class MockBusiness : public Business
{
public:
	MockBusiness(Customer& customer)
		:Business(customer)
	{

	}
	virtual ~MockBusiness() {}
	bool IsMarketEmpty() const
	{
		return m_markets.empty();
	}

	Market const& GetLastMarket() const
	{
		return m_markets.back();
	}

	bool IsTimeoutEmpty() const
	{
		return m_requestTimeouts.empty();
	}

	RequestTimeout const& GetLastRequestTimeout() const
	{
		return m_requestTimeouts.back();
	}
	using Business::AddContract;
private:
	virtual void DoOnInit()
	{

	}
	virtual void DoOnMarket(Market const& market)
	{
		m_markets.push_back(market);
	}
	virtual void DoOnPendingMarket(PendingMarket const& pendingMarket)
	{

	}
	virtual void DoOnDealing(Dealing const& dealing) 
	{

	}
	virtual void DoOnTimeout(RequestTimeout const& requestTimeout)
	{
		m_requestTimeouts.push_back(requestTimeout);
	}
	virtual void DoOnTrade(Trade const& trade)
	{

	}
	virtual void DoOnCommand(Command const& command)
	{

	}
	virtual void DoOnExchangeStatus(int exchangeStatus)
	{

	}

	std::vector<Market> m_markets;
	std::vector<RequestTimeout> m_requestTimeouts;
};

}

class BusinessThreadTest : public testing::Test
{
protected:
	virtual void SetUp()
	{
		HANDLE hQuit = INVALID_HANDLE_VALUE;
		m_customer = new Customer("","","");
		m_customer->SetOrginalAmount(10000.00);
		m_requestQueue = FastBlockQueue::Create(hQuit);
		m_businessQueue = FastBlockQueue::Create(hQuit);
		m_uiQueue = FastBlockQueue::Create(hQuit);
		m_highTimerQueue = FastBlockQueue::Create(hQuit);
		m_waitTimerQueue = FastBlockQueue::Create(hQuit);
		m_dealingRepository = new DealingRepository();
		m_queueContext = new QueueContext(*m_requestQueue,*m_businessQueue,*m_uiQueue,*m_highTimerQueue,*m_waitTimerQueue);
		m_thread = new BusinessThread(m_marketStorage,*m_queueContext,*m_dealingRepository,m_contractMatcheres,m_timeoutNotifier);
		m_marketHandler = new MockMarketHandler();
		m_business = new MockBusiness(*m_customer);


		Command command;
		command.type = Command::ADD_MARKET_HANDLER;
		command.marketHandler = m_marketHandler;
		m_thread->OnCommand(command);

		command.type = Command::ADD_BUSINESS;
		command.business = m_business;
		m_business->AddContract(0);
		m_thread->OnCommand(command);

	}
	virtual void TearDown()
	{
		delete m_marketHandler;
		delete m_thread;

		delete m_queueContext;
		delete m_dealingRepository;
		delete m_highTimerQueue;
		delete m_waitTimerQueue;
		delete m_uiQueue;
		delete m_businessQueue;
		delete m_requestQueue;
		delete m_customer;
	}
	void InitMarket()
	{
		Variety variety("rb1010",1,10);
		double lastPrice = 3000.00;
		Prices buyPrices;
		buyPrices[0] = Price(3010,10);
		Prices sellPrices;
		sellPrices[0] = Price(2990,5);
		double upperLimit = 4000.00;
		double lowerLimit = 2000.00;
		double preSettlementPrice  = 3100.00;
		Market market(0,variety,lastPrice,0,buyPrices,sellPrices,upperLimit,lowerLimit,preSettlementPrice); 

		m_marketStorage.Save(market);
	}

	Market CreateMarket0()
	{
		Variety variety("UNDEFINED",-1,-1);
		double lastPrice = 3000.00;
		Prices buyPrices;
		buyPrices[0] = Price(3010,10);
		Prices sellPrices;
		sellPrices[0] = Price(2990,5);
		double upperLimit = 4000.00;
		double lowerLimit = 2000.00;
		double preSettlementPrice  = 3100.00;
		return Market(0,variety,lastPrice,0,buyPrices,sellPrices,upperLimit,lowerLimit,preSettlementPrice); 
	}

	MarketStorage m_marketStorage;
	TimeoutNotifier m_timeoutNotifier;
	FastBlockQueue* m_requestQueue;
	FastBlockQueue* m_businessQueue;
	FastBlockQueue* m_uiQueue;
	FastBlockQueue* m_highTimerQueue;
	FastBlockQueue* m_waitTimerQueue;
	ContractMatcheres m_contractMatcheres;
	BusinessThread* m_thread;
	Customer* m_customer;
	MockBusiness* m_business;
	MockMarketHandler* m_marketHandler;
	DealingRepository* m_dealingRepository;
	QueueContext* m_queueContext;
};


static std::ostream& operator<<(std::ostream& os, Prices const& prices)
{
	return os;
}

static std::ostream& operator<<(std::ostream& os, RequestTimeout const& requestTimeout)
{
	return os;
}


TEST_F(BusinessThreadTest, AddMarketHandlerTest)
//void foo()
{
	Market market = CreateMarket0();
	m_thread->OnMarket(market);

	Market updatedMarket = m_marketStorage.GetMarket(market.GetContract());
	EXPECT_EQ(market.GetLastPrice(), updatedMarket.GetLastPrice());
	EXPECT_EQ(market.GetBuyPrices(),updatedMarket.GetBuyPrices());
	EXPECT_EQ(market.GetSellPrices(),updatedMarket.GetSellPrices());
	EXPECT_NE(market.GetCode(),updatedMarket.GetCode());

	EXPECT_EQ(false,m_marketHandler->IsEmpty());
	Market const& lastMarket = m_marketHandler->GetLastMarket();
	EXPECT_EQ(lastMarket.GetLastPrice(), updatedMarket.GetLastPrice());
	EXPECT_EQ(lastMarket.GetBuyPrices(),updatedMarket.GetBuyPrices());
	EXPECT_EQ(lastMarket.GetSellPrices(),updatedMarket.GetSellPrices());
	EXPECT_NE(lastMarket.GetCode(),updatedMarket.GetCode());

}

TEST_F(BusinessThreadTest, AddBusinessTest)
//void foo()
{
	Market market = CreateMarket0();
	m_thread->OnMarket(market);

	Market updatedMarket = m_marketStorage.GetMarket(market.GetContract());
	EXPECT_EQ(market.GetLastPrice(), updatedMarket.GetLastPrice());
	EXPECT_EQ(market.GetBuyPrices(),updatedMarket.GetBuyPrices());
	EXPECT_EQ(market.GetSellPrices(),updatedMarket.GetSellPrices());
	EXPECT_NE(market.GetCode(),updatedMarket.GetCode());

	EXPECT_EQ(false,m_business->IsMarketEmpty());
	Market const& lastMarket = m_business->GetLastMarket();
	EXPECT_EQ(lastMarket.GetLastPrice(), updatedMarket.GetLastPrice());
	EXPECT_EQ(lastMarket.GetBuyPrices(),updatedMarket.GetBuyPrices());
	EXPECT_EQ(lastMarket.GetSellPrices(),updatedMarket.GetSellPrices());
	EXPECT_NE(lastMarket.GetCode(),updatedMarket.GetCode());

}

TEST_F(BusinessThreadTest, RequestTimeoutTest)
//void foo()
{
	RequestTimeout requestTimeout(m_business,0,1,RequestTimeout::ORDER);
	m_thread->OnRequestTimeout(requestTimeout);

	EXPECT_EQ(false,m_business->IsTimeoutEmpty());
	EXPECT_EQ(requestTimeout,m_business->GetLastRequestTimeout());
}

OrderNotification CreateAccepteddNotification(Dealing const& dealing)
{
	OrderNotification on;
	on.status = OrderNotification::ACCEPT;
	on.localId = dealing.GetLocalId();
	on.contract = dealing.GetContract();
	on.direction = dealing.GetDirection();
	on.operation = dealing.GetOperation();
	on.orderQuantity = dealing.GetOrderQuantity();
	on.orderPrice = dealing.GetOrderPrice();
	return on;
}

Dealing const& CreateDealing(Customer* customer,Business* business, DealingRepository* dealingRepository)
{
	Order order = {0};
	order.businessId = business->GetId();
	order.businessPointer = business;
	strcpy_s(order.contractCode, "cu1010");
	order.contract = 0;
	order.direction = Direction::BUY;
	order.operation = Operation::OPEN;
	order.price = 1000.00;
	order.quantity = 10;
	order.localId = 1;
	return dealingRepository->Save(Dealing(*customer,order));
}

TEST_F(BusinessThreadTest, OrderedNotificationTest)
{
	Dealing const& dealing = CreateDealing(m_customer,m_business,m_dealingRepository);
	OrderNotification notification = CreateAccepteddNotification(dealing);

	m_thread->OnOrderNotification(notification);

	EXPECT_EQ(Dealing::ACCEPT, dealing.GetStatus());
}

